Repository navigation
fix(metadata)!: MetadataManager.loadMany takes a by-package name's bodies and packages off the loader's keyed entries; loadManyKeyed entries carry their package - #33299
Conversation
…oader's keyed entries Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016WV4cyrFFk86Jqf7Mp5Rzw
…s values Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016WV4cyrFFk86Jqf7Mp5Rzw
Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016WV4cyrFFk86Jqf7Mp5Rzw
📓 Docs Drift CheckThis PR changes 1 package(s): 5 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 2 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 16 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 773feb5016c00c417386d02a3a2c912b87465cd2 && git checkout 773feb5016c00c417386d02a3a2c912b87465cd2
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 9f736f2e2469d159422fb2ce1456bd932278ffff fad580c029a6fa7cdc88b22ad12f883a60ba29bd && git checkout -B drift-repro 9f736f2e2469d159422fb2ce1456bd932278ffff && git merge --no-ff fad580c029a6fa7cdc88b22ad12f883a60ba29bd
node scripts/docs-audit/affected-docs.mjs --json 9f736f2e2469d159422fb2ce1456bd932278ffff
|
|
domain:server#5 · os-elon-musk · session_016WV4cyrFFk86Jqf7Mp5Rzw |
Contract reviewServed-tier: ① Derived judgments
② Semver level
③ Boundary flags
④ Design judgmentStamping by content equality was a guess that failed silently in two measured shapes (F2, F3). Reading each keyed entry's own body and package removes the guess, and the one moved user-visible answer is the refusal the epic's red line asks for. Holds against the cited code and the card. Implemented-by: VERDICT: PASS N1 optional. |
|
domain:server#5 · os-elon-musk · session_016WV4cyrFFk86Jqf7Mp5Rzw
|
Requested by Elon · project thread
Fixes #32680
Clause-②: yes (narrowing)
Metadata-protocol: none
Skills-docs: none — no skill teaches how
MetadataManager's plural reads stamp a loader's bodies or whatloadManyKeyedentries carry; the moved answers live in the metadata service's internal read path.维护者速读
MetadataManager.loadMany对按包存放的类型,直接用加载器 keyed 条目里每行自己的 body 和包戳,不再按内容把loadMany的 body 配回行;loadManyKeyed返回的条目带上packageId。DatabaseLoader的宿主(工作区内 0 处);该形状下启动后裸读由答一个 body 改为 409 点名两个包(收窄,已声明);库里的行不动。回滚 = revert 本 PR。packages/metadata/src/metadata-manager.ts的keyedEntryBodies(风险 hunk:剩余 body 的按值比较只决定保留,不决定包);同文件keyedEntryOf;packages/metadata/src/loaders/database-loader.test.ts末尾的[#32680]describe。pnpm --filter @objectstack/metadata exec vitest run src/loaders/database-loader.test.ts -t 32680,预期 54 个用例全绿(本 PR 新增 53 个,加改写的 metadata: the boot priming pass hands a column-bound slotted row to register unstamped, so with two holders of a name the DatabaseLoader save inserts a package-less row (unbundled slotted types) #32458 用例 1 个)。What changes
MetadataManager.loadMany, for an unbundled type stored per(package, name)(PACKAGE_SLOTTED_STORE_TYPES) on a loader that holds items per package (loadManySlots:DatabaseLoader, the artifact door'sMemoryLoader), now hands on each keyed entry's own body, stamped (_packageId) with the package the loader holds that entry for when the body carries the entry's key as itsname(keyedEntryBodies). It no longer pairsloader.loadMany's bodies to the keyed entries by theirJSON.stringify(the formerwithLoaderEntryPackages). Bodies the keyed read cannot name (a row whosenamecolumn is empty) are still handed on after them, unstamped: they areloadMany's bodies less one per keyed entry whose body is the same value (key order aside), so the comparison decides only what is kept, never a package, and it runs only whenloadManyanswers more bodies than the keyed read.MetadataManager.loadManyKeyedentries carrypackageIdwhere the loader's entry carries one (keyedEntryOf), for the slotted path and the bundled one (email_template).Every moved answer, FROM to TO (the changeset carries the same list):
DatabaseLoader)MetadataManager.loadMany409 RESOURCE_CONFLICTnaming both packages (foractionalsoplatform, see notes); the same as without the passloadMany/ boot passloadMany(package, name)MetadataManager.loadManyKeyed{ name, data }{ name, data, packageId }where the loader holds it for a packageNot changed: a name one package holds with no other row's body saying it (CONTROL pins), a name no package holds, and a loader without
loadManySlots. The number of entries both plural reads hand on is unchanged for every shape but F2, where the other row's body is now kept beside both holders.Tests
New top-level describe
[#32680]inpackages/metadata/src/loaders/database-loader.test.ts(53 cases): F2 per boot-pass type with the other row stored first and last (bare read 409 naming both after the pass; reads naming ALPHA, BETA, GAMMA equal the cold read); F3 with a key-reordering loader, plus a row whose name column is empty kept once; F4 per slotted type andemail_template; one holder beside another row's body (every read equals the cold read); CONTROL one holder per type. The #32458 pin "two holders of a name and another row's body that says the same name" moves FROM[[null, ALPHA]]TO[[ALPHA, ALPHA], [BETA, BETA], [null, ALPHA]], its title rewritten to say so.BEFORE, on main
d4fbb4dbwith the same fixtures: 43 of the 53 new cases red, the 10 CONTROLs green. The F2 bare read after the pass answeredcom.alpha.appwith registry keys['helper']for every type.Downstream readers (
MetadataManager.loadMany/loadManyKeyedcallers), consumer direction: objectqlaction-governance,metadata-sync-flow-two-holders,plugin-governance-scoped-metadata,webhook-stored-row-boot-hydration(59 passed); runtimeaction-activation-unpackaged,actions-addressed-route,mcp-package-object-discovery,action-governance-scope-divergence(218 passed).Ablations (each through
scripts/ablation-replace.mjs, mutation landed on disk and restored to the HEAD blob withgit diff HEADempty; the suite readssrc/by relative import, so no build leg):loader.loadMany(no stamp at all): 42 red (the CONTROLs among them, since this drops every stamp), the 12 F4 cases green;keyedEntryOfwithoutpackageId: the 12 F4 cases red, nothing else;JSON.stringifyinstead of by value: the F3 case red (the empty-name row handed on twice);Acceptance notes
approveonaccount, BETA's oncontact, a package-less one onaccount; the cold read naming ALPHA answers ALPHA's row, after the boot pass it answers the package-less row. The mirror shape moves too: one holder ALPHA onaccountand a package-less row oncontacthand on one row (the package-less one, folded into ALPHA by the loader's per-name D4 rule), so after the pass a read naming ALPHA answers thecontactrow where the cold read answers ALPHA'saccountrow. ADR-0150 D4's action key(package, objectName, name)and its R15 row ("two holders → the ambiguous set") read the shape two ways, so it is not settled here.action, the F2 bare refusal after the pass also namesplatform: the other row (stored under its own key) is registered under its body's name in the bare slot, and an action name's package-less registry slot that no loader stores under that name counts as the platform's (metadata: in-process bare action reads disagree with the protocol on package-less holders, and a bare removal beside one package takes every slot (ADR-0150 D3/D4, follow-up of #32365) #32560). The boot pass registering a stored row under its body'snamerather than its row key is unchanged here.packages/metadata-protocol/src/**and none inpackages/metadata/src/loaders/database-loader.ts(open PR fix(metadata-protocol)!: history and rollback doors refuse a bare two-holder name and address one holder by ?package= #33193 edits it).Verification
metadata suite and typecheck on fad580c
Targeted gates run locally
Generated by Claude Code